package br.com.management.mentor.view.core;

import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;

import org.apache.commons.configuration.Configuration;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

import br.com.management.mentor.app.Config;
import br.com.management.mentor.app.components.FilesResourceLocator;
import br.com.management.mentor.app.exceptions.MenuImgIOException;
import br.com.management.mentor.app.exceptions.MenuImgNotFoundException;
import br.com.management.mentor.model.entities.auth.User;
import br.com.management.mentor.model.entities.view.Menu;
import br.com.management.mentor.model.repositories.MenusRepository;
import br.com.management.mentor.view.core.menu.MenuHandler;
import br.com.management.mentor.view.events.Event;
import br.com.management.mentor.view.events.handlers.ViewEventHandler;

public class ViewManagerImpl implements ViewManager, Observer {

	private View home;
	
	private View loginView;

	private View currentView;
	
	private Display display;
	
	private Shell mainWindow;
	
	private ToolBar toolbar;

	private Composite viewsContainer;

	@Autowired
	private Locale locale;

	@Autowired
	private Configuration configuration;
	
	@Autowired
	private MenusRepository menusRepository;
	
	@Autowired
	private ViewExceptionHandler exceptionHandler;
	
	@Autowired
	private FilesResourceLocator fileResourceLocator;
	
	@Autowired
	private ApplicationContext context;
	
	private Set<ViewEventHandler> eventHandlers;
	
	private Set<MenuHandler> menuHandlers;
	
	@Override
	public void start() {
		buildMainWindow();
		buildMenus();
		buildViewContainer();

		goToView( getHome() );

		mainWindow.pack();
		mainWindow.open();
		while ( !mainWindow.isDisposed() ) {
			try {
				if ( !display.readAndDispatch() ) display.sleep();
			} catch( Exception e ) {
				exceptionHandler.handle( e , currentView );
			}
			
		}
		display.dispose();
	}

	private void buildViewContainer() {
		Composite view = new Composite( mainWindow , SWT.BORDER );

		double percentage = configuration.getDouble( Config.VIEWS_HEIGHT_PERCENTAGE.name() );
		view.setLayout( new RowLayout() );
		view.setLayoutData( new RowData( mainWindow.getBounds().width , (int) ( mainWindow.getBounds().height * percentage ) ) );

		setViewsContainer( view );
	}

	public void buildMenus() {
		if( toolbar == null ) toolbar = new ToolBar( mainWindow , SWT.FLAT );
		else for ( ToolItem t : toolbar.getItems() ) t.dispose();
		
		double percentage = configuration.getDouble( Config.VIEWS_MENUS_PERCENTAGE.name() );
		toolbar.setSize( mainWindow.getBounds().width , ( int ) ( mainWindow.getBounds().height * percentage ) );

		List<Menu> menus = null;

		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		if ( authentication != null && authentication.isAuthenticated() ) {
			User user = (User) authentication.getPrincipal();
			menus = menusRepository.getByUser( user );
		} else {
			menus = menusRepository.getPublics();
		}

		for ( final Menu menu : menus ) {
			ToolItem item = new ToolItem( toolbar , SWT.PUSH );
			item.setText( context.getMessage( menu.getKey() , null , locale ) );

			item.addSelectionListener( new SelectionListener() {
				
				@Override
				public void widgetSelected( SelectionEvent e ) {
					for ( MenuHandler handler : menuHandlers ) {
						if( !handler.canHandle( menu ) ) continue;
						
						handler.handle( menu );
						break;
					}
				}
				
				@Override
				public void widgetDefaultSelected( SelectionEvent e ) { }
			} );
			
			String imgURL = fileResourceLocator.getMenuImgURL( menu.getImage() );
			Resource resource = context.getResource( imgURL );
			if ( !resource.exists() ) throw new MenuImgNotFoundException( new Object[] { menu.getImage() , menu.getKey() } );

			try {
				item.setImage( new Image( display , resource.getFile().getAbsolutePath() ) );
			} catch ( IOException e ) {
				throw new MenuImgIOException( new String[] { menu.getImage() , menu.getKey() } );
			}
		}

		toolbar.update();
	}

	@Override
	public void goToView( View view ) {
		clearViewsContainer();
		
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		boolean userLogged = authentication != null && authentication.isAuthenticated(); 
			
		if ( userLogged ) currentView = view;
		else currentView = getLoginView();

		currentView.addObserver( this );
		currentView.build( getViewsContainer() );
	}
	
	private void clearViewsContainer() {
		for ( Control c : getViewsContainer().getChildren() ) c.dispose();
		getViewsContainer().update();
	}

	@Override
	public void update( Observable o , Object arg ) {
		if( arg == null || !Event.class.isAssignableFrom( arg.getClass() ) ) return;
		if( !View.class.isAssignableFrom( o.getClass() ) ) return;
		
		Event event = (Event) arg;
		View view = (View) o;
		for ( ViewEventHandler handler : eventHandlers ) {
			if( !handler.canHandle( event ) ) continue;
			
			handler.handle( view , event );
			break;
		}
	}

	private void buildMainWindow() {
		Shell shell = new Shell( display );

		RowLayout layout = new RowLayout();
		layout.type = SWT.VERTICAL;
		layout.wrap = false;

		shell.setLayout( layout );

		mainWindow = shell;
	}

	public ViewManagerImpl() {
		display = new Display();
	}

	public View getHome() {
		return home;
	}

	public void setHome( View home ) {
		this.home = home;
	}

	public View getLoginView() {
		return loginView;
	}

	public void setLoginView( View loginView ) {
		this.loginView = loginView;
	}

	public Set<ViewEventHandler> getEventHandlers() {
		return eventHandlers;
	}

	public void setEventHandlers( Set<ViewEventHandler> eventHandlers ) {
		this.eventHandlers = eventHandlers;
	}

	public Set<MenuHandler> getMenuHandlers() {
		return menuHandlers;
	}

	public void setMenuHandlers( Set<MenuHandler> menuHandlers ) {
		this.menuHandlers = menuHandlers;
	}

	@Override
	public void close() {
		display.close();
	}

	public Composite getViewsContainer() {
		return viewsContainer;
	}

	public void setViewsContainer( Composite viewsContainer ) {
		this.viewsContainer = viewsContainer;
	}
	
}	